lib/gpg: Add more specific OstreeGpgError codes
authorDan Nicholson <nicholson@endlessm.com>
Sat, 15 Jun 2019 14:56:44 +0000 (09:56 -0500)
committerDan Nicholson <nicholson@endlessm.com>
Fri, 24 Jan 2020 20:02:17 +0000 (13:02 -0700)
Currently `ostree_gpg_verify_result_require_valid_signature` always
returns an error that the key used for the signature is missing from the
keyring. However, all that's been determined is that there are no valid
signatures. The error could also be from an expired signature, an
expired key, a revoked key or an invalid signature.

Provide values for these missing errors and return them from
`ostree_gpg_verify_result_require_valid_signature`. The description of
each result is appended to the error message, but since the result can
contain more than one signature but only a single error can be returned,
the status of the last signature is used for the error code. See the
comment for rationale.

Related: flatpak/flatpak#1450

src/libostree/ostree-gpg-verify-result.c
src/libostree/ostree-gpg-verify-result.h
tests/test-pull-summary-sigs.sh
tests/test-remote-gpg-import.sh

index 63c3394aa6b12efc9b48c36748406ad8e5d5584b..67270c829a60ee74730ea109e491eb61e81bea1f 100644 (file)
@@ -769,8 +769,58 @@ ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result,
 
   if (ostree_gpg_verify_result_count_valid (result) == 0)
     {
-      g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_MISSING_KEY,
-                   "GPG signatures found, but none are in trusted keyring");
+      /*
+       * Join the description of each failed signature for the error message.
+       * Only one error code can be returned, so if there was more than one
+       * signature, use the error of the last one under the assumption that
+       * it's the most recent and hopefully most likely to be made with a
+       * valid key.
+       */
+      gint code = OSTREE_GPG_ERROR_NO_SIGNATURE;
+      g_autoptr(GString) buffer = g_string_sized_new (256);
+      guint nsigs = ostree_gpg_verify_result_count_all (result);
+
+      if (nsigs == 0)
+        /* In case an empty result was passed in */
+        g_string_append (buffer, "No GPG signatures found");
+      else
+        {
+          for (int i = nsigs - 1; i >= 0; i--)
+            {
+              g_autoptr(GVariant) info = ostree_gpg_verify_result_get_all (result, i);
+              ostree_gpg_verify_result_describe_variant (info, buffer, "",
+                                                         OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
+
+              if (i == nsigs - 1)
+                {
+                  gboolean key_missing, key_revoked, key_expired, sig_expired;
+                  g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_MISSING,
+                                       "b", &key_missing);
+                  g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_REVOKED,
+                                       "b", &key_revoked);
+                  g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_EXPIRED,
+                                       "b", &key_expired);
+                  g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_SIG_EXPIRED,
+                                       "b", &sig_expired);
+
+                  if (key_missing)
+                    code = OSTREE_GPG_ERROR_MISSING_KEY;
+                  else if (key_revoked)
+                    code = OSTREE_GPG_ERROR_REVOKED_KEY;
+                  else if (key_expired)
+                    code = OSTREE_GPG_ERROR_EXPIRED_KEY;
+                  else if (sig_expired)
+                    code = OSTREE_GPG_ERROR_EXPIRED_SIGNATURE;
+                  else
+                    /* Assume any other issue is a bad signature */
+                    code = OSTREE_GPG_ERROR_INVALID_SIGNATURE;
+                }
+            }
+        }
+
+      /* Strip any trailing newlines */
+      g_strchomp (buffer->str);
+      g_set_error_literal (error, OSTREE_GPG_ERROR, code, buffer->str);
       return FALSE;
     }
 
index 7c71ecdca92c833e94339b4fe8d298789cfa9f27..f71ab981a2143676943ec60ab3f442442b16e200 100644 (file)
@@ -159,6 +159,11 @@ gboolean ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult
  * @OSTREE_GPG_ERROR_NO_SIGNATURE: A signature was expected, but not found.
  * @OSTREE_GPG_ERROR_INVALID_SIGNATURE: A signature was malformed.
  * @OSTREE_GPG_ERROR_MISSING_KEY: A signature was found, but was created with a key not in the configured keyrings.
+ * @OSTREE_GPG_ERROR_EXPIRED_SIGNATURE: A signature was expired. Since: 2019.7.
+ * @OSTREE_GPG_ERROR_EXPIRED_KEY: A signature was found, but the key used to
+ *   sign it has expired. Since: 2019.7.
+ * @OSTREE_GPG_ERROR_REVOKED_KEY: A signature was found, but the key used to
+ *   sign it has been revoked. Since: 2019.7.
  *
  * Errors returned by signature creation and verification operations in OSTree.
  * These may be returned by any API which creates or verifies signatures.
@@ -169,6 +174,9 @@ typedef enum {
   OSTREE_GPG_ERROR_NO_SIGNATURE = 0,
   OSTREE_GPG_ERROR_INVALID_SIGNATURE,
   OSTREE_GPG_ERROR_MISSING_KEY,
+  OSTREE_GPG_ERROR_EXPIRED_SIGNATURE,
+  OSTREE_GPG_ERROR_EXPIRED_KEY,
+  OSTREE_GPG_ERROR_REVOKED_KEY,
 } OstreeGpgError;
 
 /**
index 821ae95309b595d2437ee62e212982ecbe77242d..401e88c9145ad71eb63a0b19f32876e656ac1252 100755 (executable)
@@ -189,7 +189,7 @@ cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.2,}
 if ${OSTREE} --repo=repo pull origin main 2>err.txt; then
     assert_not_reached "Successful pull with old summary"
 fi
-assert_file_has_content err.txt "none are in trusted keyring"
+assert_file_has_content err.txt "BAD signature"
 assert_has_file repo/tmp/cache/summaries/origin
 assert_has_file repo/tmp/cache/summaries/origin.sig
 cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2
index 4d73fa116459584ba62a42a964fff65f0e30aa66..b8673852d2112b4839a0c525fc4d45234092706f 100755 (executable)
@@ -163,7 +163,7 @@ ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key1.asc,${test_tmp
 if ${OSTREE} pull R8:main 2>err.txt; then
     assert_not_reached "Unexpectedly succeeded at pulling with different key"
 fi
-assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring"
+assert_file_has_content err.txt "public key not found"
 
 # Test gpgkeypath success with directory containing a valid key
 ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/ R9 $(cat httpd-address)/ostree/gnomerepo
@@ -243,7 +243,7 @@ ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key2.asc R6 $(cat h
 if ${OSTREE} pull R6:main 2>err.txt; then
     assert_not_reached "Unexpectedly succeeded at pulling with different key"
 fi
-assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring"
+assert_file_has_content err.txt "public key not found"
 
 echo "ok"
 
@@ -269,7 +269,7 @@ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo rev-par
 if ${OSTREE} pull --require-static-deltas R1:main 2>err.txt; then
     assert_not_reached "Unexpectedly succeeded at pulling commit signed with untrusted key"
 fi
-assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring"
+assert_file_has_content err.txt "public key not found"
 
 echo "ok gpg untrusted signed commit for delta upgrades"